package tool.code.generater.template;

import lombok.Getter;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import tool.code.generater.util.StrKit;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Velocity模板引擎
 *
 * @author zhoup
 * @since 2025/8/18
 */
public class VelocityTemplate {
    @Getter
    private VelocityEngine velocityEngine;
    private final String basePath;
    private String mode;
    public final static String COPY_SUFFIX = ".copy";
    public VelocityTemplate(String templatePath) {
        this.basePath = templatePath;
        init();
    }

    public void init() {
        // 1. 配置引擎（指定模板加载路径等）
        Properties props = new Properties();
        // 设置模板文件所在的目录（例如：classpath 下的 templates 文件夹）
//        props.setProperty("resource.loader", "class");
        props.setProperty("resource.loader", "file");
        props.setProperty("file.resource.loader.path", this.basePath);
        props.setProperty("file.resource.loader.encoding", "UTF-8");
//        props.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        props.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
        velocityEngine = new VelocityEngine(props);
        velocityEngine.init();
        this.mode = System.getProperty("ddlCoderConfig.template.mode", "create");
    }

    public Template getTemplate(String templateName) {
//        if (StringUtils.isEmpty(basePath)) {
//            return velocityEngine.getTemplate(templateName);
//        }
        return velocityEngine.getTemplate(templateName);
    }

    public void render(String templateName, Map<String, Object> templateContext, File outputFile) throws IOException {
        Template template = getTemplate(templateName);
        VelocityContext context = new VelocityContext(templateContext);
        context.put("StrKit", StrKit.class);
        boolean isUpdateMode = "update".equalsIgnoreCase(this.mode);
        if (isUpdateMode) {
            File copyFile = new File(outputFile.getAbsolutePath() + COPY_SUFFIX);
            try {
                Files.copy(outputFile.toPath(), copyFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                System.out.printf("文件：%s 复制异常", outputFile.getAbsolutePath());
            }
        }
        // 创建临时文件用于存储模板生成的内容
        File tempFile = File.createTempFile("velocity_", ".tmp");
        try {
            try (FileWriter fileWriter = new FileWriter(tempFile)) {
                template.merge(context, fileWriter);
            }
            // 如果输出文件不存在，直接复制临时文件内容
            if (!outputFile.exists() || !isUpdateMode) {
                Files.copy(tempFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            } else {
                // 增量更新：只更新标记部分的代码
                updateFileWithMarkers(tempFile, outputFile);
            }
        } finally {
            // 删除临时文件
            tempFile.delete();
        }

    }

    /**
     * 使用标记方法增量更新文件
     * 只更新 <!--/// AUTO-GENERATED START --> 和 <!--/// AUTO-GENERATED END --> 之间的内容
     * 或者 /// AUTO-GENERATED START 和 /// AUTO-GENERATED END 之间的内容
     */
    private void updateFileWithMarkers(File tempFile, File outputFile) throws IOException {
        // 读取生成的模板内容
        String generatedContent = Files.readString(tempFile.toPath());

        // 读取原文件内容
        String originalContent = Files.readString(outputFile.toPath());

        // 检查原文件是否包含任何标记
        boolean hasMarkersInOriginal = hasAnyMarkers(originalContent);
        
        // 如果原文件中没有标记，则直接用新生成的文件覆盖
        if (!hasMarkersInOriginal) {
            Files.copy(tempFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            return;
        }

        // 更新后的内容
        String updatedContent = originalContent;

        // 处理HTML/XML注释标记 <!--/// AUTO-GENERATED START -->...<!--/// AUTO-GENERATED END -->
        updatedContent = replaceMarkedContent(updatedContent, generatedContent,
                "<!--///\\s*AUTO-GENERATED START\\s*-->",
                "<!--///\\s*AUTO-GENERATED END\\s*-->");

        // 处理单行注释标记 // AUTO-GENERATED START...// AUTO-GENERATED END
        updatedContent = replaceMarkedContent(updatedContent, generatedContent,
                "///\\s*AUTO-GENERATED START\\s*",
                "///\\s*AUTO-GENERATED END\\s*");

        // 处理多行注释标记 /* AUTO-GENERATED START */.../* AUTO-GENERATED END */
        updatedContent = replaceMarkedContent(updatedContent, generatedContent,
                "/\\*///\\s*AUTO-GENERATED START\\s*\\*/",
                "/\\*///\\s*AUTO-GENERATED END\\s*\\*/");

        // 写入更新后的内容
        try (FileWriter writer = new FileWriter(outputFile)) {
            writer.write(updatedContent);
        }
    }

    /**
     * 检查内容中是否包含任何标记
     */
    private boolean hasAnyMarkers(String content) {
        // 检查HTML/XML注释标记
        Pattern htmlPattern = Pattern.compile("<!--///\\s*AUTO-GENERATED START\\s*-->", Pattern.DOTALL);
        if (htmlPattern.matcher(content).find()) {
            return true;
        }
        
        // 检查单行注释标记
        Pattern singleLinePattern = Pattern.compile("///\\s*AUTO-GENERATED START\\s*", Pattern.DOTALL);
        if (singleLinePattern.matcher(content).find()) {
            return true;
        }
        
        // 检查多行注释标记
        Pattern multiLinePattern = Pattern.compile("/\\*///\\s*AUTO-GENERATED START\\s*\\*/", Pattern.DOTALL);
        return multiLinePattern.matcher(content).find();
    }

    /**
     * 替换标记间的内容
     * 从generatedContent中提取标记间的内容，替换originalContent中对应的标记间内容
     */
    private String replaceMarkedContent(String originalContent, String generatedContent,
                                        String startPattern, String endPattern) {
        // 在新生成的内容中查找标记间的内容
        Pattern generatedPattern = Pattern.compile("(" + startPattern + ")(.*?)(" + endPattern + ")",
                Pattern.DOTALL);
        Matcher generatedMatcher = generatedPattern.matcher(generatedContent);

        while (generatedMatcher.find()) {
            // 提取新生成的标记内容
            String startMarker = generatedMatcher.group(1);
            String markedContent = generatedMatcher.group(2);
            String endMarker = generatedMatcher.group(3);
            
            // 构造完整的标记块
            String replacement = startMarker + markedContent + endMarker;
            
            // 在原文件中查找并替换对应的标记块
            Pattern originalPattern = Pattern.compile(
                "(" + Pattern.quote(startMarker) + ").*?(" + Pattern.quote(endMarker) + ")", 
                Pattern.DOTALL);
            
            originalContent = originalPattern.matcher(originalContent)
                .replaceFirst(Matcher.quoteReplacement(replacement));
        }
        
        return originalContent;
    }
}
